#include <errno.h>
#include "client.h"
#include "util.h"
#include <fcntl.h>
#include <openssl/err.h>
#include <openssl/evp.h>
extern toml_table_t *conf;

char *get_hash(const char *filename, const char *algorithm) {
  int fd = open(filename, O_RDONLY);
  if (fd < 0) {
    perror("打开文件失败");
    return NULL;
  }

  EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
  if (mdctx == NULL) {
    fprintf(stderr, "EVP_MD_CTX_new 创建失败\n");
    close(fd);
    return NULL;
  }

  /* 使用 EVP_MD_fetch 获取指定的哈希算法 */
  EVP_MD *md = EVP_MD_fetch(NULL, algorithm, NULL);
  if (md == NULL) {
    fprintf(stderr, "EVP_MD_fetch 获取算法失败: %s\n", algorithm);
    EVP_MD_CTX_free(mdctx);
    close(fd);
    return NULL;
  }

  if (EVP_DigestInit_ex(mdctx, md, NULL) != 1) {
    fprintf(stderr, "EVP_DigestInit_ex 初始化失败\n");
    EVP_MD_free(md);
    EVP_MD_CTX_free(mdctx);
    close(fd);
    return NULL;
  }

  unsigned char buffer[4096];
  ssize_t bytes_read;
  while ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) {
    if (EVP_DigestUpdate(mdctx, buffer, bytes_read) != 1) {
      fprintf(stderr, "EVP_DigestUpdate 处理数据失败\n");
      EVP_MD_free(md);
      EVP_MD_CTX_free(mdctx);
      close(fd);
      return NULL;
    }
  }
  if (bytes_read < 0) {
    perror("读取文件失败");
    EVP_MD_free(md);
    EVP_MD_CTX_free(mdctx);
    close(fd);
    return NULL;
  }
  close(fd);

  unsigned int digest_len = 0;
  unsigned char *digest = OPENSSL_malloc(EVP_MD_size(md));
  if (digest == NULL) {
    fprintf(stderr, "内存分配失败\n");
    EVP_MD_free(md);
    EVP_MD_CTX_free(mdctx);
    return NULL;
  }
  if (EVP_DigestFinal_ex(mdctx, digest, &digest_len) != 1) {
    fprintf(stderr, "EVP_DigestFinal_ex 计算哈希失败\n");
    OPENSSL_free(digest);
    EVP_MD_free(md);
    EVP_MD_CTX_free(mdctx);
    return NULL;
  }

  EVP_MD_free(md);
  EVP_MD_CTX_free(mdctx);

  /* 将二进制哈希转换为16进制字符串 */
  char *hash_hex = malloc(digest_len * 2 + 1);
  if (hash_hex == NULL) {
    OPENSSL_free(digest);
    return NULL;
  }
  for (unsigned int i = 0; i < digest_len; i++) {
    sprintf(&hash_hex[i * 2], "%02x", digest[i]);
  }
  hash_hex[digest_len * 2] = '\0';
  OPENSSL_free(digest);
  return hash_hex;
}

int verify_hash(const char *filename, const char *algorithm,
                const char *expected_hash) {
  char *computed_hash_hex = get_hash(filename, algorithm);
  if (computed_hash_hex == NULL) {
    return -1;
  }
  int result = (strcasecmp(computed_hash_hex, expected_hash) == 0) ? 1 : 0;
  free(computed_hash_hex);
  return result;
}


int conf_init(toml_table_t **conf) {
  FILE *fp;
  char errbuf[200];
  fp = fopen("../conf.toml", "r");
  if (!fp) {
    fprintf(stderr, "cannot open sample.toml - %s", strerror(errno));
    return -1;
  }
  *conf = toml_parse_file(fp, errbuf, sizeof(errbuf));
  fclose(fp);
  if (!conf) {
    fprintf(stderr, "cannot parse - %s", errbuf);
    return -1;
  }
  return 0;
}

int conf_client(client_configure *s) {

  toml_table_t *client = toml_table_in(conf, "Client");
  if (!client) {
    fprintf(stderr, "missing [client].\n");
    return -1;
  }
  toml_datum_t host = toml_string_in(client, "host");
  if (!host.ok) {
    fprintf(stderr, "cannot read client.host.\n");
    return -1;
  }
  toml_datum_t port = toml_string_in(client, "port");
  if (!port.ok) {
    fprintf(stderr, "cannot read client.port.\n");
    return -1;
  }
  toml_datum_t port2 = toml_string_in(client, "port2");
  if (!port2.ok) {
    fprintf(stderr, "cannot read server.port2.\n");
    return -1;
  }
 
  strcpy(s->host, host.u.s);
  strcpy(s->port, port.u.s);
  strcpy(s->port2, port2.u.s);

  free(host.u.s);
  free(port.u.s);
  free(port2.u.s);

  return 0;
}

